package designmode.proxy;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理模式
 * 在代理模式（Proxy Pattern）中，一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
 * 在代理模式中，我们创建具有现有对象的对象，以便向外界提供功能接口。
 * <p>
 * 代理模式是最常使用的模式之一了，用一个代理来隐藏具体实现类的实现细节，通常还用于在真实的实现的前后添加一部分逻辑。
 * 既然说是代理，那就要对客户端隐藏真实实现，由代理来负责客户端的所有请求。当然，代理只是个代理，它不会完成实际的业务逻辑，而是一层皮而已，
 * 但是对于客户端来说，它必须表现得就是客户端需要的真实实现。
 * <p>
 * <p>
 * 代理模式说白了就是做 “方法包装” 或做 “方法增强”。在面向切面编程中，其实就是动态代理的过程。比如 Spring 中，我们自己不定义代理类，但是
 * Spring 会帮我们动态来定义代理，然后把我们定义在 @Before、@After、@Around 中的代码逻辑动态添加到代理中。
 * 说到动态代理，又可以展开说，Spring 中实现动态代理有两种，一种是如果我们的类定义了接口，如 UserService 接口和 UserServiceImpl 实现，
 * 那么采用 JDK 的动态代理，感兴趣的读者可以去看看 java.lang.reflect.Proxy 类的源码；另一种是我们自己没有定义接口的，Spring 会采用 CGLIB 进行动态代理，它是一个 jar 包，性能还不错。
 */
interface Image {
    void display();
}

interface Subject {
    String operate1();

    String operate2();

    String operate3();

    String operate4();

    String operate5();
}

class RealImage implements Image {

    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(fileName);
    }

    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }

    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }
}

class ProxyImage implements Image {

    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

class ProxyPatternDemo {

    public static void main(String[] args) {
        Image image = new ProxyImage("test_10mb.jpg");

        // 图像将从磁盘加载
        image.display();
        System.out.println("");
        // 图像不需要从磁盘加载
        image.display();
    }
}

/**
 * 静态代理
 */

class Main {
    public static void main(String[] args) {
        Proxy.newProxyInstance(ProxyImage.class.getClassLoader(), ProxyImage.class.getInterfaces(), (proxy, method, args1) -> {
            if (method.getName() == null) {
                System.out.println(111);
            }
            method.invoke(proxy, method, args1);
            return null;
        });
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return null;
            }
        };
        Enhancer enhancer = new Enhancer();


        Subject subject = (Subject) new ProxyHandler().newProxyInstance(new RealSubject1());
        Subject subject2 = (Subject) new ProxyHandler().newProxyInstance(new RealSubject2());
        subject.operate2();
        subject2.operate4();
    }

}

class RealSubject1 implements Subject {
    @Override
    public String operate1() {
        return "RealSubject-operate1()";
    }

    @Override
    public String operate2() {
        return "RealSubject-operate2()";
    }

    @Override
    public String operate3() {
        return "RealSubject-operate3()";
    }

    @Override
    public String operate4() {
        return "RealSubject-operate4()";
    }

    @Override
    public String operate5() {
        return "RealSubject-operate5()";
    }
}

class RealSubject2 implements Subject {
    @Override
    public String operate1() {
        return "RealSubject2-operate1()";
    }

    @Override
    public String operate2() {
        System.out.println("RealSubject2-operate2()");
        return "RealSubject2-operate2()";
    }

    @Override
    public String operate3() {
        return "RealSubject2-operate3()";
    }

    @Override
    public String operate4() {
        return "RealSubject2-operate4()";
    }

    @Override
    public String operate5() {
        return "RealSubject2-operate5()";
    }
}

class ProxyHandler implements InvocationHandler {
    Object obj = null;

    public Object newProxyInstance(Object realObj) {
        this.obj = realObj;
        Class<?> classType = this.obj.getClass();
        return Proxy.newProxyInstance(classType.getClassLoader(), classType.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("I'm Proxy, I'm invoking...invoke前");
        Object object = method.invoke(obj, args);
        System.out.println("invoking----" + object);
        System.out.println(method.getName());
        return object;
    }
}


/**
 * cglib动态代理
 */

class ProxyCglib implements MethodInterceptor {
    private Object target;

    public static void main(String[] args) {
        ProxyCglib proxy = new ProxyCglib();
        RealSubjectCglib cglib = (RealSubjectCglib) proxy.getInstance(new RealSubjectCglib());
        cglib.operate();
        cglib.operate2();
    }

    public Object getInstance(Object target) {
        this.target = target;
        //Cglib中的加强器，用来创建动态代理
        Enhancer enhancer = new Enhancer();
        //设置要创建动态代理的类
        enhancer.setSuperclass(this.target.getClass());
        //设置回调，这里相当于是对于代理类上所有方法的调用，都会调用Callback，而Callback则需要实现intercept()方法进行拦截
        enhancer.setCallback(this);
        Object obj = enhancer.create();
        return obj;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("I'm Proxy, I'm invoking...");
        Object object = proxy.invokeSuper(obj, args);
        System.out.println(object);
        return null;
    }
}

class RealSubjectCglib {
    public String operate() {
        System.out.println("测试是否会直接调用我");
        return "RealSubjectCglib";
    }


    public String operate2() {
        System.out.println("方法二");
        return "RealSubjectCglib";
    }
}

